package webDevelopment.tcp;

import webDevelopment.util.Log;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// 长连接模式下，不能读取完一次请求就挂断电话
// 挂断电话的主动权交到客户端手中
// 客户端不挂电话，服务器就坚决不挂电话！！
// 表现在代码中，我们怎么就知道对方挂电话了？
// 我们是通过 InputStream 进行读取的
// 所以，当我们读到了 EOS，就代表，对方挂断电话了
// 区分：对方本次发送了个 0 个数据  n == 0
//      对方挂电话了： n == -1（只是一个信号，不是一个数据）
// EOS 表现到 scanner 中，就是 hasNextLine() 会返回 false
public class TranslateServerLongConnectionThreadPool {
    public static final int PORT = 8888;

    // 把电话接通之后，和用户对接的过程，专门提出一个任务出来
    private static class 负责配客户聊天直到客户主动挂电话 implements Runnable {
        private final Socket socket;
        // 这里的 Socket 代表着已经接通的电话
        public 负责配客户聊天直到客户主动挂电话(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                // 对方信息：
                InetAddress inetAddress = socket.getInetAddress();  // ip
                Log.println("对方的 ip: " + inetAddress);
                int port = socket.getPort();    // port
                Log.println("对方的 port: " + port);
                SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();    // ip + port
                Log.println("对方的 ip + port: " + remoteSocketAddress);

                InputStream inputStream = socket.getInputStream();
                OutputStream outputStream = socket.getOutputStream();
                // 构造的过程只需要一次
                Scanner scanner = new Scanner(inputStream, "UTF-8");
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
                PrintWriter writer = new PrintWriter(outputStreamWriter);

                while (true) {
                    Log.println("等待客户端 " + remoteSocketAddress + " 的请求 或者 挂电话");
                    if (!scanner.hasNextLine()) {
                        Log.println("对方挂电话了");
                        // 代表 scanner 背后的 InputStream 遇到了 EOS(-1)
                        // 也就代表对方挂电话了
                        // 所以我们也可以退出了
                        break;  // 只会跳出内部循环
                    }
                    Log.println("对方来数据了");
                    // 读取请求
                    String request = scanner.nextLine();    // nextLine() 就会去掉换行符
                    String engWord = request;
                    Log.println("英文: " + engWord);

                    // 翻译
                    String chiWord = translate(engWord);
                    Log.println("中文: " + chiWord);

                    // 发送响应
                    String response = chiWord;  // TODO: 响应的单词中是没有 \r\n

                    Log.println("准备发送");
                    writer.printf("%s\r\n", response);

                    writer.flush();
                    Log.println("发送成功");
                }

                // 挂掉电话
                socket.close();
                Log.println("挂断电话");
            } catch (Exception exc) {
                // 把受查异常变成非受查异常，就不会提示语法错误了
                throw new RuntimeException(exc);    // exc 是 RuntimeException 的原因（cause）
            }
        }
    }

    public static void main(String[] args) throws Exception {
        Log.println("启动长连接 + 多线程版本的 TCP 服务器");
        // 固定长度的线程池：同时对接的人，最多就是 8 个
//        ExecutorService service = Executors.newFixedThreadPool(8);
        // 没有线程上限，可以无限对接
        ExecutorService service = Executors.newCachedThreadPool();

        initMap();
        ServerSocket serverSocket = new ServerSocket(PORT);
        while (true) {
            // 接电话
            Log.println("等待对方来连接");
            Socket socket = serverSocket.accept();  // 主线程只负责接电话
            Log.println("有客户端连接上来了");

            Runnable task = new 负责配客户聊天直到客户主动挂电话(socket);

            // 把任务交给一个专门的线程去处理
//            new Thread(task).start();
            service.execute(task);
        }
//        serverSocket.close();
    }


    private static final HashMap<String, String> map = new HashMap<>();

    private static void initMap() {
        map.put("apple", "苹果");
        map.put("pear", "梨");
        map.put("orange", "橙子");
    }

    private static String translate(String engWord) {
        String chiWord = map.getOrDefault(engWord, "查无此单词");
        return chiWord;
    }
}
